Skip to content

fix(libc): find a relinked module by stem when the exact name misses (carries #6226) - #8626

Merged
oneandonlydean merged 4 commits into
boykopovar:mainfrom
xXPBSXx:carry/6226-relinker-stem
Oct 21, 2026
Merged

oneandonlydean merged 4 commits into
boykopovar:mainfrom
xXPBSXx:carry/6226-relinker-stem

Conversation

@xXPBSXx

@xXPBSXx xXPBSXx commented Oct 20, 2026

Copy link
Copy Markdown
Contributor

What

Carries #6226 (@lendinglibrary) onto current main, keeping the original commit and its author (a3c4cdcdbb), and adds one commit that addresses the review points on #6226 (the author has not pushed since 15 October). The problem and the Unity IL2CPP evidence are those described in #6226: a title asks for a module by its console name, the relinker names the converted module after the ELF it was built from, so RelinkedModulePath misses and sceKernelLoadStartModule fails with 0x80020002; after the exact <name>.guest.prx and the case-insensitive lookup both miss, the lookup takes the sibling with the same stem that ends with .guest.prx.

Changes on top of the original commit:

Fixes: none. #6226 stays open so its author can answer; if the author prefers to push the review fixes there, this PR can be closed.

Tested

Windows 10 (19045), MinGW-w64 GCC 15.2.0, Release, on main + this branch:

  • guest_dynamic_loader_variant_names: fails (failed at GuestDynamicLoaderVariants.cpp:22, exit 0xc0000409) with the cut at the first dot (new case kept) and passes with the last extension; guest_dynamic_loader and guest_filesystem pass, 3 of 3 runs.
  • python tools/check_conventions.py --base upstream/main: 0 errors, 1 warning (debt-length: the libc.md entry over 1000 characters comes from the original commit).
  • Overlap: git merge-tree of this branch with fix(libc): find a relinked module by stem when the exact name misses #6226 has three conflicts (the same code, expected); with upstream/main it is clean. pr_overlap was not run.
  • Not tested: Linux/macOS, a title run (the Unity run is the evidence in fix(libc): find a relinked module by stem when the exact name misses #6226).

Checklist

  • Based on current main; no other open PR implements the same functions
  • One topic per PR; follow-ups go in a new PR
  • No comments in code except technical debt
  • Unimplemented paths throw; no silent stubs added
  • No notes, investigation .md files or images added to the repository
  • New third-party code is a submodule built from source (none added)
  • Generic behaviour, not specific to one title
  • Depends on: none (carries fix(libc): find a relinked module by stem when the exact name misses #6226)
  • Unblocks: none
  • AI-assisted: yes

AI-assisted (Claude Code).

🤖 Generated with Claude Code

lendinglibrary and others added 2 commits October 20, 2026 20:42
A title asks for a module by its console name and the relinker names the
converted module after the ELF it was built from: Unity IL2CPP titles load
Media/Modules/<name>.prx and Media/Plugins/<name>.prx while the dump holds
<name>.elf.guest.prx, so sceKernelLoadStartModule failed with 0x80020002
and the title stopped 3 s after start. After the exact and the
case-insensitive <name>.guest.prx lookups miss, take the sibling whose stem
matches the request and ends with .guest.prx, shortest name first.
…n only

The stem of a request and of a relinked module name was cut at the first dot,
so a request for Engine.Render.prx that missed could load Engine.Core.prx.guest.prx.
Only the last extension is removed now, as the review of the stem lookup asked,
and the variant-names test requests a dotted name that must not load the sibling
fixture module.

Co-Authored-By: lendinglibrary <340867775+lendinglibrary@users.noreply.github.com>
Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@github-actions

github-actions Bot commented Oct 20, 2026 •

Copy link
Copy Markdown

Other open pull requests touch the same code. Merging one will break the other, or both implement the same thing. If this one builds on them, list them in Depends on.

Pull request Conflicting files (lines on main) Same exports Same new files
PR 6226 core/libs/tests/GuestDynamicLoaderVariants.cpp
PR 6544 core/libs/prx/libc/src/General.cpp L677-680

Some comparisons could not be completed because a pull request conflicts with the base branch. Review these shared files manually before concluding that there is no overlap.

Pull request Shared files not compared
PR 3965 docs/dev/debt/libc.md
PR 5021 core/libs/prx/libc/src/General.cpp
core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libc.md
PR 6044 core/libs/prx/libkernel/CMakeLists.txt
PR 6226 core/libs/prx/libc/src/General.cpp
core/libs/prx/libkernel/CMakeLists.txt
core/libs/tests/GuestDynamicLoaderVariants.cpp
docs/dev/debt/libc.md
PR 6469 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libc.md
PR 6819 docs/dev/debt/libc.md
PR 6936 core/libs/prx/libkernel/CMakeLists.txt
PR 6977 core/libs/prx/libkernel/CMakeLists.txt
PR 7081 docs/dev/debt/libc.md
PR 7192 core/libs/prx/libkernel/CMakeLists.txt
PR 7201 core/libs/prx/libc/src/General.cpp
core/libs/prx/libkernel/CMakeLists.txt
PR 7532 core/libs/prx/libkernel/CMakeLists.txt
PR 7648 docs/dev/debt/libc.md
PR 7715 docs/dev/debt/libc.md
PR 8150 docs/dev/debt/libc.md
PR 8520 core/libs/prx/libkernel/CMakeLists.txt
docs/dev/debt/libc.md
PR 8711 docs/dev/debt/libc.md

Comment thread core/libs/prx/libkernel/CMakeLists.txt Outdated
WORKING_DIRECTORY "$<TARGET_FILE_DIR:guest_module_fixture>" RESOURCE_LOCK guest_dynamic_loader_files)
endif()
file(MAKE_DIRECTORY "${CMAKE_CURRENT_BINARY_DIR}/variant_names")
file(WRITE "${CMAKE_CURRENT_BINARY_DIR}/variant_names/guest_module_fixture.sprx.elf.GUEST.PRX" "not a module\n")

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This fixture no longer exercises the shortest-match or dangling-symlink-before-valid-candidate paths after the last-extension change. For the request guest_module_fixture.prx, SameRelinkedStem strips .GUEST.PRX and then only the last extension: guest_module_fixture.sprx.elf.GUEST.PRX yields stem guest_module_fixture.sprx, and the symlink name ending in .zprx.elf.GUEST.PRX yields guest_module_fixture.zprx. Neither matches guest_module_fixture; only the copied .elf.GUEST.PRX file matches. Could the decoy and symlink instead be named e.g. guest_module_fixture.sprx.GUEST.PRX and guest_module_fixture.zprx.GUEST.PRX? Those share the requested stem under this rule and would make the selection/error-path coverage real.

… for the stem

With the stem cut at the last extension, the decoy guest_module_fixture.sprx.elf.GUEST.PRX
and the dangling link guest_module_fixture.zprx.elf.GUEST.PRX had the stems
guest_module_fixture.sprx and guest_module_fixture.zprx, so they no longer
matched the request and the shortest-name rule was not exercised. They are now
guest_module_fixture.sprx.GUEST.PRX and guest_module_fixture.zprx.GUEST.PRX,
which have the request's stem and are longer than the real module copy.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@xXPBSXx

xXPBSXx commented Oct 20, 2026

Copy link
Copy Markdown
Contributor Author

Good catch, thank you. After the cut at the last extension the decoy and the dangling link no longer had the request's stem, so the shortest-name rule was not exercised any more. I renamed them as you suggested (guest_module_fixture.sprx.GUEST.PRX, guest_module_fixture.zprx.GUEST.PRX; commit c983777). To check that they compete again I flipped the length comparison in RelinkedModulePath's stem lookup: guest_dynamic_loader_variant_names then fails at the first dlopen, and it passes with the real comparison (Windows 10, MinGW-w64 GCC 15.2, Release). The dangling-link variant only exists on non-Windows hosts and I did not run it.
AI-assisted (Claude Code).

@oneandonlydean

Copy link
Copy Markdown
Collaborator

@xXPBSXx I merged current main into this branch (2de9386). Besides a keep-both in General.cpp, the merge commit adds one guard in MatchNames.

#7467 added an early return to MatchNamesWindows for case-insensitive directories (the NTFS default). That path looks up each name exactly and never reaches SameRelinkedStem, so on Windows this PR's stem fallback would never run. The merge now takes the Windows fast path only when no requested name has stem set:

bool stem = false;
for (const auto& name : names) stem = stem || name.stem;
if (!stem && CaseInsensitiveDirectoryWindows(directory)) return MatchNamesWindows(directory, names);

ResolveHostPath never sets stem, so #7467's fast path is unchanged for ordinary lookups. Only RelinkedModulePath goes back to the iterator. guest_dynamic_loader_variant_names passing on Windows CI will confirm it.

@github-actions github-actions Bot removed the needs rebase Conflicts with main, rebase needed label Oct 21, 2026
Co-authored-by: xXPBSXx <20614131+xXPBSXx@users.noreply.github.com>
@oneandonlydean
oneandonlydean force-pushed the carry/6226-relinker-stem branch from 2de9386 to 8cb7251 Compare October 21, 2026 06:28
@oneandonlydean

Copy link
Copy Markdown
Collaborator

Note: I force-pushed the merge commit as 8cb7251b2e to correct its committer identity. The tree is identical to the previous head 2de93860ab, so nothing in the code changed. CI on the new head is next.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants